/****************************************************************************
** @brief
****************************************************************************/

#include <QSerialPort>

#include "cardZlg.h"

#include "QDebug"
#define CARD_DEBUG

static uint8_t s_thaisen_encryption_key[6] = {0x72, 0x28, 0x92, 0x63, 0x46, 0x23};

CardZlg::CardZlg(enum CardType cardType, QObject *parent) : AbstractCard(parent)
{
    card_state_ = AbstractCard::kCardIdle;
    reader_type_ = AbstractCard::KZlg;
    card_type_ = cardType;
    card_number_ = "";
    card_num_len_ = 0;

    serialport_ = new QSerialPort;
    if (nullptr == serialport_) {
        return;
    }

#ifndef __arm__
    return;
#endif

    connect(serialport_, &QSerialPort::readyRead, this, &CardZlg::HandleSerialData);

    serialport_->setPortName("/dev/ttyS3");
    serialport_->setBaudRate(115200);
    serialport_->setDataBits(QSerialPort::Data8);
    serialport_->setParity(QSerialPort::NoParity);
    serialport_->setStopBits(QSerialPort::OneStop);
    serialport_->setFlowControl(QSerialPort::NoFlowControl);

    serialport_->close();
    if (serialport_->open(QIODevice::ReadWrite)) {
#if defined (CARD_DEBUG)
        qDebug() << "[ZLG]-> card device open success";
#endif
    } else {
#if defined (CARD_DEBUG)
        qDebug() << "[ZLG]-> card device open fail";
#endif
    }
}

CardZlg::~CardZlg()
{

}

void CardZlg::TestSerial()
{
    if (nullptr == serialport_) {
        return;
    }

    if (!serialport_->isOpen()) {
        return;
    }

    QByteArray data;

    data[0] = 0x20;

    qint64 result = serialport_->write(data, 0x01);
#if defined (CARD_DEBUG)
    qDebug() << "[ZLG]-> serial test result:" << result;
#endif
}

void CardZlg::SearchCard()
{
    if (nullptr == serialport_) {
        set_card_state(AbstractCard::KCardOffline);
        return;
    }

    if (!serialport_->isOpen()) {
        set_card_state(AbstractCard::KCardOffline);
        return;
    }

    serialport_->write(Construct(kMifare, kActionCard)); /* 构造卡激活数据 */

#if defined (CARD_DEBUG)
    //qDebug() << "[ZLG]-> search card...";
#endif
}

void CardZlg::HandleSerialData()
{
    QByteArray read_data;

    //接收完成的串口数据
    read_data.clear();
    read_data = serialport_->readAll();
    while (serialport_->waitForReadyRead(20)) {
        read_data += serialport_->readAll();
    }

#if defined (CARD_DEBUG)
    //qDebug() << "[ZLG]-> serial data:" << read_data.toHex(' ').toUpper();
#endif

    //开始校验数据 (一帧数据的最小长度: 8 + 0 + 2)
    if (8 + 0 + 2 > read_data.count()) {
        set_card_state(AbstractCard::KCardOffline);
        return;
    }

    //校验成功了，就开始处理数据
    QByteArray block_data;
    if (kMifare == read_data.at(0x03)) {
        switch (cmd_code_) {
        case kActionCard:
            /* 进行状态检测（成功还是失败） */
            if (0 != read_data.at(0x04) + (read_data.at(0x05) << 8)) {
                set_card_state(AbstractCard::kCardIdle);
                return;
            }
            /* 进行数据长度的检测 */
            if (8 + 2 + 1 + 1 + 0 + 2 > read_data.count()) {
                set_card_state(AbstractCard::KCardOffline);
                return;
            }
            if (8 + 2 + 1 + 1 + read_data.at(11) + 2 > read_data.count()) {
                set_card_state(AbstractCard::KCardOffline);
                return;
            }
            /* 从 INFO 数据中获取到 UID 数据 */
            uid_.clear();
            uid_len_ = 0;
            for (int i = 0; i < read_data.at(11); i++) {
                uid_.append(read_data.at(12 + i));
                uid_len_ = uid_len_ + 1;
            }
#if defined (CARD_DEBUG)
            qDebug() << "[ZLG]-> uid data:" << uid_.toHex(' ').toUpper();
#endif
            switch (card_type_) {
            case kAnyue:
                break;
            case kThaisen:
                serialport_->write(Construct(kMifare, kMFAuthent)); /* 构造直接密钥验证数据 */
                break;
            case KYunkuaichong:
                /* 从 UID 数据中获取到卡号数据 */
                card_number_.clear();
                card_num_len_ = 0;
                for (int i = 0; i < uid_len_; i++) {
                    card_number_.append(uid_.at(i));
                    card_num_len_ = card_num_len_ + 1;
                }

                emit HandleCardNumber(card_number_); /* 获取到了卡号，发信号处理数据 */
                break;
            case KThaisenAndYunkuaichong:
                serialport_->write(Construct(kMifare, kMFAuthent)); /* 构造直接密钥验证数据 */
                break;
            default:
                break;
            }
            break;
        case kMFAuthent:
            serialport_->write(Construct(kMifare, kReadM1DataBlock)); /* 构造 Mifare 卡读数据 */
            break;
        case kReadM1DataBlock:
            switch (card_type_) {
            case kAnyue:
                break;
            case kThaisen:
                /* 进行状态检测（成功还是失败） */
                if (0 != read_data.at(0x04) + (read_data.at(0x05) << 8)) {
                    set_card_state(AbstractCard::KCardOffline);
                    return;
                }
                /* 进行数据长度的检测 */
                if (8 + 16 + 2 > read_data.count()) {
                    set_card_state(AbstractCard::KCardOffline);
                    return;
                }
                /* 拷贝有用的 INFO 数据 */
                block_data.clear();
                for (int i = 0; i < 16; i++) {
                    block_data.append(read_data.at(8 + i));
                }
#if defined (CARD_DEBUG)
                qDebug() << "[ZLG]-> block data:" << block_data.toHex(' ').toUpper();
#endif
                /* 从 INFO 数据中计算出或者获取到卡号数据 */
                card_number_.clear();
                card_num_len_ = 0;
                for (int i = 0; i < 16; i++) {
                    card_number_.append(block_data.at(i));
                    card_num_len_ = card_num_len_ + 1;
                }

                emit HandleCardNumber(card_number_); /* 获取到了卡号，发信号处理数据 */

                break;
            case KYunkuaichong:
                break;
            case KThaisenAndYunkuaichong:
                /* 拷贝有用的 INFO 数据 */
                block_data.clear();
                for (int i = 0; i < 16 && i < read_data.size() - 8; i++) {
                    block_data.append(read_data.at(8 + i));
                }
#if defined (CARD_DEBUG)
                qDebug() << "[ZLG]-> block data:" << block_data.toHex(' ').toUpper();
#endif
                /* 从 INFO 数据中计算出或者获取到卡号数据 */
                card_number_.clear();
                card_num_len_ = 0;
                if (16 > block_data.size()) {
                    /* Yunkuaichong */
                    for (int i = 0; i < uid_len_; i++) {
                        card_number_.append(uid_.at(i));
                        card_num_len_ = card_num_len_ + 1;
                    }
                } else {
                    /* Taixiangchong */
                    for (int i = 0; i < 16 && i < block_data.size(); i++) {
                        card_number_.append(block_data.at(i));
                        card_num_len_ = card_num_len_ + 1;
                    }
                }

                emit HandleCardNumber(card_number_); /* 获取到了卡号，发信号处理数据 */

                break;
            default:
                break;
            }
            break;
        default:
            break;
        }
    } // if (kMifare == cmd_class)
}

unsigned int CardZlg::CalcSum(unsigned char *data, unsigned int len)
{
    unsigned int checksum = 0;

    for (unsigned int i = 0; i < len; i++) {
        checksum += data[i];
    }

    return (~checksum);
}

QByteArray CardZlg::Construct(CardZlg::CardClass cmd_class, CardZlg::CardCmd cmd_code)
{
    unsigned int  sum = 0;
    unsigned char data[128] = {0};
    QByteArray    construct_data;

    if (kMifare == cmd_class) {
        switch (cmd_code) {
        case kActionCard:
            data[0] = 0xB2;
            data[1] = 0x00;
            data[2] = 0x00;
            data[3] = cmd_class;
            data[4] = cmd_code;
            data[5] = 0x00;

            data[6] = 0x02;
            data[7] = 0x00;

            data[8] = 0x00;
            data[9] = 0x26;

            sum = CalcSum(&data[0], 9 + 1);
            data[10] = (sum  & 0xFF);
            data[11] = (sum >> 0x08) & 0xFF;

            construct_data.clear();
            for (int i = 0; i < 11 + 1; i++) {
                construct_data.append(data[i]);
            }
            break;
        case kMFAuthent:
            data[0] = 0xB2;
            data[1] = 0x61;
            data[2] = 0x6E;
            data[3] = cmd_class;
            data[4] = cmd_code;
            data[5] = 0x00;

            data[6] = 0x0C;
            data[7] = 0x00;

            switch (card_type_) {
            case kAnyue:
                break;
            case kThaisen:
                data[ 8] = 0x60;
                data[ 9] = uid_[0];
                data[10] = uid_[1];
                data[11] = uid_[2];
                data[12] = uid_[3];
                data[13] = s_thaisen_encryption_key[0]; //key start...
                data[14] = s_thaisen_encryption_key[1];
                data[15] = s_thaisen_encryption_key[2];
                data[16] = s_thaisen_encryption_key[3];
                data[17] = s_thaisen_encryption_key[4];
                data[18] = s_thaisen_encryption_key[5]; //key end...
                data[19] = 0x09; /* 9号扇区 */
                break;
            case KYunkuaichong:
                break;
            case KThaisenAndYunkuaichong:
                data[ 8] = 0x60;
                data[ 9] = uid_[0];
                data[10] = uid_[1];
                data[11] = uid_[2];
                data[12] = uid_[3];
                data[13] = s_thaisen_encryption_key[0]; //key start...
                data[14] = s_thaisen_encryption_key[1];
                data[15] = s_thaisen_encryption_key[2];
                data[16] = s_thaisen_encryption_key[3];
                data[17] = s_thaisen_encryption_key[4];
                data[18] = s_thaisen_encryption_key[5]; //key end...
                data[19] = 0x09; /* 9号扇区 */
                break;
            default:
                break;
            }

            sum = CalcSum(&data[0], 19 + 1);
            data[20] = (sum  & 0xFF);
            data[21] = (sum >> 0x08) & 0xFF;

            construct_data.clear();
            for (int i = 0; i < 21 + 1; i++) {
                construct_data.append(data[i]);
            }
            break;
        case kReadM1DataBlock:
            data[0] = 0xB2;
            data[1] = 0x00;
            data[2] = 0x00;
            data[3] = cmd_class;
            data[4] = cmd_code;
            data[5] = 0x00;

            data[6] = 0x01;
            data[7] = 0x00;

            switch (card_type_) {
            case kAnyue:
                break;
            case kThaisen:
                data[8] = 0x09; /* 9号扇区 */
                break;
            case KYunkuaichong:
                break;
            case KThaisenAndYunkuaichong:
                data[8] = 0x09; /* 9号扇区 */
                break;
            default:
                break;
            }

            sum = CalcSum(&data[0], 8 + 1);
            data[ 9] = (sum  & 0xFF);
            data[10] = (sum >> 0x08) & 0xFF;

            construct_data.clear();
            for (int i = 0; i < 10 + 1; i++) {
                construct_data.append(data[i]);
            }
            break;
        default:
            break;
        }

        cmd_code_ = cmd_code;
    }

    return construct_data;
}
